package com.yuandian.management.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuandian.api.management.entity.ConfMpOrg;
import com.yuandian.api.management.po.ConfMpOrganizationParams;
import com.yuandian.api.management.vo.ConfMpOrgExcelVo;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.bpm.common.excel.vo.ErrorMessage;
import com.yuandian.management.enums.OrgTypeEnum;
import com.yuandian.management.mapper.ConfMpOrgMapper;
import com.yuandian.management.service.ConfMpOrgService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 中台监控-内部单位配置表-Service实现
 * @author zfsn
 * @date 2023-6-8
 */
@Service
public class ConfMpOrgServiceImpl extends ServiceImpl<ConfMpOrgMapper, ConfMpOrg>
        implements ConfMpOrgService {

    /**
     * 通过唯一性编码获取单位配置
     * @param orgCode 单位编码
     * @return 单位配置实体
     */
    private ConfMpOrg getByUniqueCode(String orgCode) {
        return lambdaQuery().eq(ConfMpOrg::getOrgCode, orgCode).one();
    }

    /**
     * 分页查询内部单位
     * @param params 中台监控-单位配置请求参数
     * @return 通用返回对象
     */
    @Override
    public R<Page<ConfMpOrg>> orgPage(ConfMpOrganizationParams<ConfMpOrg> params) {
        Page<ConfMpOrg> page = Page.of(params.getCurrent(), params.getSize());
        page.addOrder(params.orders()).orders().forEach(orderItem -> orderItem.setColumn(StrUtil.toUnderlineCase(orderItem.getColumn())));
        return R.ok(lambdaQuery().page(page));
    }

    /**
     * 新增内部单位
     * @param params 中台监控-单位配置请求参数
     * @return 通用返回对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R<Boolean> orgSave(ConfMpOrganizationParams<ConfMpOrg> params) {
        // 1、校验编码组合唯一性
        if (ObjectUtil.isNotNull(getByUniqueCode(params.getOrgCode()))) {
            return R.failed("新增失败，已存在相同的单位编码！");
        }
        // 2、新增内部单位配置
        ConfMpOrg confMpOrg = BeanUtil.copyProperties(params, ConfMpOrg.class);
        save(confMpOrg);
        return R.ok(Boolean.TRUE, "新增成功");
    }

    /**
     * 主键修改内部单位
     * @param params 中台监控-单位配置请求参数
     * @return 通用返回对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R<Boolean> orgUpdateById(ConfMpOrganizationParams<ConfMpOrg> params) {
        // 1、校验请求参数
        Integer id = params.getId();
        if (id == null) {
            return R.failed("更新失败，单位id不能为空！");
        }
        // 2、通过id获取单位配置
        ConfMpOrg confMpOrg = getById(id);
        // 3、校验该配置是否存在
        if (ObjectUtil.isNull(confMpOrg)) {
            return R.failed("更新失败，不存在该单位配置！");
        }
        // 4、校验编码组合的唯一性
        ConfMpOrg confMpOrg1 = getByUniqueCode(params.getOrgCode());
        if (ObjectUtil.isNotNull(confMpOrg1) && !id.equals(confMpOrg1.getId())) {
            return R.failed("更新失败，已存在相同的单位编码！");
        }
        // 5、更新单位配置
        BeanUtil.copyProperties(params, confMpOrg);
        updateById(confMpOrg);
        return R.ok(Boolean.TRUE, "更新成功");
    }

    /**
     * 主键删除内部单位
     * @param idList 主键集合
     * @return 通用返回对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R<Boolean> orgRemoveByIds(List<Integer> idList) {
        // 1、校验请求参数
        if (CollUtil.isEmpty(idList)) {
            return R.failed("删除失败，主键集合不能为空");
        }
        // 2、删除单位配置
        removeByIds(idList);
        return R.ok(Boolean.TRUE, "删除成功");
    }

    /**
     * 导入内部单位配置
     * @param excelVoList 导入的数据集
     * @param bindingResult 用来校验 实体 jsr303 校验失败结果
     * @return 通用返回对象
     */
    @Override
    public R<List<ErrorMessage>> importOrg(List<ConfMpOrgExcelVo> excelVoList, BindingResult bindingResult) {
        // 1、校验请求参数
        if (CollUtil.isEmpty(excelVoList)) {
            return R.failed("导入失败，Excel为空或解析失败！");
        }
        // 2、获取外部配置唯一性校验集
        Map<String, Integer> uniqueMap = lambdaQuery().select(ConfMpOrg::getId, ConfMpOrg::getOrgCode)
                .list().stream().collect(Collectors.toMap(ConfMpOrg::getOrgCode, ConfMpOrg::getId, (k1, k2) -> k1));
        // 3、遍历导入的数据集
        long failCount = 0;
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        assert errorMessageList != null;
        Map<String, ConfMpOrg> excelOrgMap = new HashMap<>();
        for (ConfMpOrgExcelVo excelVo : excelVoList) {
            Set<String> errorMsg = new HashSet<>();
            String orgCode = excelVo.getOrgCode();
            String orgName = excelVo.getOrgName();
            String orgType = excelVo.getOrgType();

            // 非空校验：单位编码、单位名称
            if (!StrUtil.isAllNotBlank(orgCode, orgName, orgType)) {
                errorMsg.add("单位非空校验不通过，请检查以下字段：单位编码、单位名称、单位类型");
                errorMessageList.add(new ErrorMessage(excelVo.getLineNum(), errorMsg));
                failCount++;
                continue;
            }
            ConfMpOrg.ConfMpOrgBuilder builder = ConfMpOrg.builder();
            // 唯一校验：单位编码唯一
            Integer id = uniqueMap.get(orgCode);
            if (id != null) {
                // 存在重复的单位编码，处理方式是覆盖更新，所以这里创建实体对象时需要回填id
                builder.id(id);
            }
            // 通过校验，创建实体对象添加到待入库集合中
            excelOrgMap.put(orgCode, builder
                    .orgCode(orgCode)
                    .orgName(orgName)
                    .orgType(OrgTypeEnum.getItemKey(orgType))
                    .build());
        }
        // 4、批量写入外部单位配置表
        Collection<ConfMpOrg> orgList = excelOrgMap.values();
        if (CollUtil.isNotEmpty(orgList)) {
            saveOrUpdateBatch(orgList, 1000);
        }
        return R.ok(errorMessageList, "导入完成，失败条数：" + failCount);
    }
}
